package czt.jiami;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @author czt
 * @version 1.0
 * @since 2025/3/13
 */
public class RSATest {

    public static String publicKeyPath;
    public static String privateKeyPath;

    /**
     * rsa单次最大加密的明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * rsa单次最大解密的密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    static {
        ClassLoader classLoader = RSATest.class.getClassLoader();
        publicKeyPath = classLoader.getResource("rsa.pub").getPath();
        privateKeyPath = classLoader.getResource("rsa.pri").getPath();
    }

    /**
     * 生成公钥和私钥写入文件
     */
    private static void generateKeyPair() throws Exception{
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // 生成公钥
        PublicKey publicKey = keyPair.getPublic();
        // 把对象转成字节数组
        byte[] publicKeyEncoded = publicKey.getEncoded();
        // 使用base64编码
        String publicKeyBase64String = Base64.encodeBase64String(publicKeyEncoded);

        // 生成私钥
        PrivateKey privateKey = keyPair.getPrivate();
        // 把对象转成字节数组
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 使用base64编码
        String privateKeyBase64String = Base64.encodeBase64String(privateKeyEncoded);

        // 公钥和私钥写入文件
        FileUtils.writeStringToFile(new File(publicKeyPath), publicKeyBase64String, StandardCharsets.UTF_8);
        FileUtils.writeStringToFile(new File(privateKeyPath), privateKeyBase64String, StandardCharsets.UTF_8);
    }

    public static void main(String[] args) throws Exception{
        generateKeyPair();
    }


    /**
     * 加密
     * @param text 原文
     * @param key 公钥或私钥
     * @return 密文
     */
    private static String encrypt(String text, Key key) throws Exception{
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        // 加解密实现方法
        byte[] bytes = doCodec(cipher, text.getBytes(StandardCharsets.UTF_8), MAX_ENCRYPT_BLOCK);
        // 编码，返回Base64字符串
        return Base64.encodeBase64String(bytes);
    }

    /**
     * 解密
     * @param encryptText 密文
     * @param key 公钥或私钥
     * @return 原文
     */
    private static String decrypt(String encryptText, Key key) throws Exception{
        // 先解码
        byte[] decodeBase64 = Base64.decodeBase64(encryptText);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, key);
        // 加解密实现方法
        byte[] bytes = doCodec(cipher, decodeBase64, MAX_DECRYPT_BLOCK);
        return new String(bytes, StandardCharsets.UTF_8);
    }


    /**
     * 加解密
     * @param cipher 加解密模式
     * @param bytes 原文或密文数组
     * @return 原文或密文数组
     */
    private static byte[] doCodec(Cipher cipher, byte[] bytes, int maxInt) throws Exception{
        int inputLen = bytes.length;
        int offset = 0;
        byte[] cache;
        int i = 0;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 循环分段处理
        while ((inputLen - offset) > 0) {
            if ((inputLen - offset) > maxInt) {
                cache = cipher.doFinal(bytes, offset, maxInt);
            } else {
                cache = cipher.doFinal(bytes, offset, inputLen - offset);
            }
            baos.write(cache, 0, cache.length);
            i++;
            offset = i * maxInt;
        }
        byte[] byteArray = baos.toByteArray();
        baos.close();
        return byteArray;
    }

    /**
     * 获取公钥(base64编码后的)
     * @return 公钥串
     */
    private static PublicKey getPublicKey() throws Exception{
        String encryptKey = FileUtils.readFileToString(new File(publicKeyPath), StandardCharsets.UTF_8);
        byte[] bytes = Base64.decodeBase64(encryptKey);
        // 公钥规则 x509
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(bytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(x509EncodedKeySpec);
    }

    /**
     * 获取私钥
     * @return 私钥串
     */
    private static PrivateKey getPrivateKey() throws Exception{
        String encryptKey = FileUtils.readFileToString(new File(privateKeyPath), StandardCharsets.UTF_8);
        byte[] bytes = Base64.decodeBase64(encryptKey);
        // 私钥规则 pkcs8
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    }
}
